<template>
	<view class="hb-circle">
		<canvas class="hb-circle__canvas" :style="setCircleStyle" :canvas-id="ctxId"></canvas>
		<view v-if=" !text " class="hb-circle__text">
			<slot></slot>
		</view>
		<cover-view v-else class="hb-circle__text">{{ text }}</cover-view>
	</view>
</template>

<script>
	function format(rate) {
		return Math.min(Math.max(rate, 0), 100);
	}
	//周长
	const PERIMETER = 2 * Math.PI;
	//开始角度
	const BEGIN_ANGLE = -Math.PI / 2;
	//步长
	const STEP = 1;
	//默认初始颜色
	const BLUE = "#1989fa";
	import {
		isObj
	} from "@/utils/object"
	export default {
		name: "hb-circle",
		props: {
			ctxId: {
				type: String,
				default: 'hb-circle'
			},
			//文字
			text: String,
			//目标进度,	进度圈百分比值 - 显示范围0-100 ，可能数比较大就需要自己转成百分比的值
			value: {
				type: Number,
				default: 0,
				validator: val => {
					return val >= 0 && val <= 100;
				},
			},
			//圆形进度条画布宽度
			size: {
				type: Number,
				default: 100
			},
			//动画速度（单位为 value/s）
			speed: {
				type: Number,
				default: 50
			},
			//填充颜色
			fill: String,
			//进度条颜色，默认为 蓝色
			color: {
				type: [String, Object],
				default: BLUE
			},
			//轨道颜色，默认白色
			layerColor: {
				type: String,
				default: "#fff"
			},
			/**
			 * 圈两端的形状 可选：'round', 'square'
			 */
			lineCap: {
				type: String,
				default: "round",
				validator: val => {
					return ['round', 'square'].includes(val);
				},
			},
			//轨道宽度
			strokeWidth: {
				type: Number,
				default: 4
			},
			//是否顺时针增加
			clockwise: {
				type: Boolean,
				default: true
			}
		},
		data() {
			return {
				hoverColor: BLUE
			}
		},
		computed: {
			setCircleStyle() {
				return `width: ${this.size}px;
							height: ${this.size}px;`
			},
		},
		watch: {
			// 监听进度条百分比值改变
			value: {
				deep: true, // 深度监听
				handler(newVal, oldVal) {
					this.reRender()
				}
			},
			//监听进度条颜色变动
			color: {
				deep: true,
				handler(newVal, oldVal) {
					this.setHoverColor()
				}
			}
		},
		methods: {
			getContext() {
				if (!this.ctx) {
					this.ctx = uni.createCanvasContext(this.ctxId, this);
				}
				return this.ctx
			},
			clearInterval() {
				if (this.interval) {
					clearInterval(this.interval);
					this.interval = null;
				}
			},
			/**
			 * 动态设置进度条颜色
			 */
			setHoverColor() {
				const ctx = this.getContext();
				const {
					color,
					size
				} = this.$props;
				if (isObj(color)) {
					const LinearColor = ctx.createLinearGradient(size, 0, 0, 0);
					Object.keys(color).sort((a, b) => parseFloat(a) - parseFloat(b)).map(key => LinearColor.addColorStop(parseFloat(
						key) / 100, color[key]))
				}
				this.hoverColor = color;
				this.drawCircle(this.currentValue)
			},
			/**
			 * 预设画布
			 * @param {Object} ctx
			 * @param {Object} strokeStyle
			 * @param {Object} beginAngle
			 * @param {Object} endAngle
			 * @param {Object} fill
			 */
			presetCanvas(ctx, strokeStyle, beginAngle, endAngle, fill) {
				const {
					strokeWidth,
					lineCap,
					clockwise,
					size
				} = this.$props;
				const position = size / 2;
				const radius = position - strokeWidth / 2;
				ctx.setStrokeStyle(strokeStyle);
				ctx.setLineWidth(strokeWidth);
				ctx.setLineCap(lineCap);
				ctx.beginPath()
				ctx.arc(position, position, radius, beginAngle, endAngle, !clockwise)
				ctx.stroke()
				if (fill) {
					ctx.setFillStyle(fill)
					ctx.fill()
				}
			},
			/**
			 * 渲染图层为圆形，轨道图层
			 * @param {Object} ctx
			 */
			renderLayerCircle(ctx) {
				const {
					layerColor,
					fill
				} = this.$props;
				this.presetCanvas(ctx, layerColor, 0, PERIMETER, fill)
			},
			/**
			 * 渲染悬停圆圈，进度条图层
			 * @param {Object} ctx
			 * @param {Object} formatValue
			 */
			renderHoverCircle(ctx, formatValue) {
				const {
					clockwise
				} = this.$props
				//结束角度
				const progress = PERIMETER * (formatValue / 100);
				const endAngle = clockwise ? BEGIN_ANGLE + progress : 3 * Math.PI - (BEGIN_ANGLE + progress);
				this.presetCanvas(ctx, this.hoverColor, BEGIN_ANGLE, endAngle)
			},
			/**
			 * 开始画图
			 * @param {Object} currentValue
			 */
			drawCircle(currentValue) {
				const ctx = this.getContext();
				ctx.clearRect(0, 0, this.size, this.size)
				this.renderLayerCircle(ctx);
				const formatValue = format(currentValue)
				if (formatValue !== 0) {
					this.renderHoverCircle(ctx, formatValue);
				}
				ctx.draw()
			},
			/**
			 * value 值变动时，重画进度条
			 */
			reRender() {
				const {
					value,
					speed
				} = this.$props
				if (speed <= 0 || speed > 1000) {
					this.drawCircle(value);
					return;
				}
				this.clearInterval()
				this.currentValue = this.currentValue || 0;
				this.interval = setInterval(() => {
					if (this.currentValue !== value) {
						if (this.currentValue < value) {
							this.currentValue += STEP;
						} else {
							this.currentValue -= STEP
						}
						this.drawCircle(this.currentValue)
					} else {
						this.clearInterval();
					}
				}, 1000 / speed)
			}
		},
		created() {
			const {
				value,
				color
			} = this.$props;
			this.currentValue = value;
			this.hoverColor = color
			this.$nextTick(() => {
				this.drawCircle(this.value);
			})
		},
		destroyed() {
			this.ctx = null;
			this.clearInterval();
		}
	}
</script>

<style lang="less">
	.hb-circle {
		position: relative;
		display: inline-block;
		text-align: center;

		&__text {
			position: absolute;
			top: 50%;
			left: 0;
			width: 100%;
			transform: translateY(-50%);
			color: #323233
		}
	}
</style>
